package com.tianji.promotion.utils;

import com.tianji.common.exceptions.BizIllegalException;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
@RequiredArgsConstructor
public class MyLockAspect implements Ordered {

    private final RedissonClient redissonClient;

    @Around("@annotation(myLock)")
    public Object tryLock(ProceedingJoinPoint pjp, MyLock myLock) throws Throwable {
        // 获取方法参数,处理{userId}
        Object[] args = pjp.getArgs();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Parameter[] parameters = method.getParameters();

        String lockName = parse(myLock.name(), parameters, args);


        // 1.创建锁对象,默认使用创建可重入锁
        RLock lock = redissonClient.getLock(lockName);

        // 2.尝试获取锁
        boolean isLock = lock.tryLock(myLock.waitTime(), myLock.leaseTime(), myLock.unit());

        // 3.判断是否成功
        if(!isLock) {
            throw new BizIllegalException("请求太频繁");
        }
        try {
            //TimeUnit.SECONDS.sleep(30);
            return pjp.proceed();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 用于替换参数里面的userId，注意单词不要拼错
     * 比如lock:coupon:{userId} 会替换成lock:coupon:2,如果userId为2的话
     * @param expression
     * @param parameters
     * @param args
     * @return
     */
    public static String parse(String expression, Parameter[] parameters, Object[] args) {
        String lockName = expression;

        for (int i = 0; i < parameters.length; i++) {
            String paramName = parameters[i].getName();
            if ("userId".equals(paramName)) {
                Object paramValue = args[i];
                lockName = lockName.replace("{userId}", paramValue.toString());
                break;
            }
        }

        return lockName;
    }


}
